agora inbox for [email protected]  
help / color / mirror / Atom feed
all keyword for pg_hba.conf host column
1264+ messages / 4 participants
[nested] [flat]

* all keyword for pg_hba.conf host column
@ 2010-10-16 10:56 Peter Eisentraut <[email protected]>
  2010-10-16 15:03 ` Re: all keyword for pg_hba.conf host column Robert Haas <[email protected]>
  2010-10-16 15:11 ` Re: all keyword for pg_hba.conf host column Brendan Jurd <[email protected]>
  0 siblings, 2 replies; 1264+ messages in thread

From: Peter Eisentraut @ 2010-10-16 10:56 UTC (permalink / raw)
  To: pgsql-hackers

As a small addition to the pg_hba.conf host name feature, I figured it
would be useful to allow "all" in the host column, instead of having to
write 0.0.0.0/0 and ::/0.  Patch attached.


Attachments:

  [text/x-patch] hba-host-all.patch (1.7K, ../../[email protected]/2-hba-host-all.patch)
  download | inline diff:
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index ab96af8..228cfff 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -257,7 +257,7 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
       </para>
 
       <para>
-       You can also write
+       You can also write <literal>all</literal> to match any IP address,
        <literal>samehost</literal> to match any of the server's own IP
        addresses, or <literal>samenet</literal> to match any address in any
        subnet that the server is directly connected to.
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 3f50349..38eaa95 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -879,8 +879,11 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
 		}
 		token = lfirst(line_item);
 
-		/* Is it equal to 'samehost' or 'samenet'? */
-		if (strcmp(token, "samehost\n") == 0)
+		if (strcmp(token, "all\n") == 0)
+		{
+			parsedline->ip_cmp_method = ipCmpAll;
+		}
+		else if (strcmp(token, "samehost\n") == 0)
 		{
 			/* Any IP on this host is allowed to connect */
 			parsedline->ip_cmp_method = ipCmpSameHost;
@@ -1497,6 +1500,8 @@ check_hba(hbaPort *port)
 							continue;
 					}
 					break;
+				case ipCmpAll:
+					break;
 				case ipCmpSameHost:
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index eb6637f..aa60d8d 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -36,7 +36,8 @@ typedef enum IPCompareMethod
 {
 	ipCmpMask,
 	ipCmpSameHost,
-	ipCmpSameNet
+	ipCmpSameNet,
+	ipCmpAll
 } IPCompareMethod;
 
 typedef enum ConnType


^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* Re: all keyword for pg_hba.conf host column
  2010-10-16 10:56 all keyword for pg_hba.conf host column Peter Eisentraut <[email protected]>
@ 2010-10-16 15:03 ` Robert Haas <[email protected]>
  1 sibling, 0 replies; 1264+ messages in thread

From: Robert Haas @ 2010-10-16 15:03 UTC (permalink / raw)
  To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers

On Oct 16, 2010, at 6:56 AM, Peter Eisentraut <[email protected]> wrote:
> As a small addition to the pg_hba.conf host name feature, I figured it
> would be useful to allow "all" in the host column, instead of having to
> write 0.0.0.0/0 and ::/0.  Patch attached.

+1. Looks sane on a quick read.

...Robert



^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* Re: all keyword for pg_hba.conf host column
  2010-10-16 10:56 all keyword for pg_hba.conf host column Peter Eisentraut <[email protected]>
@ 2010-10-16 15:11 ` Brendan Jurd <[email protected]>
  1 sibling, 0 replies; 1264+ messages in thread

From: Brendan Jurd @ 2010-10-16 15:11 UTC (permalink / raw)
  To: Peter Eisentraut <[email protected]>; +Cc: pgsql-hackers

On 16 October 2010 21:56, Peter Eisentraut <[email protected]> wrote:
> As a small addition to the pg_hba.conf host name feature, I figured it
> would be useful to allow "all" in the host column, instead of having to
> write 0.0.0.0/0 and ::/0.  Patch attached.
>

Cool.  And, for what it's worth, this doesn't conflict at all with the
field-specific keywords patch I just submitted.

Cheers,
BJ



^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v2 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5d78252a1..e2948ac966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -334,18 +334,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1100bdec1..0647955005 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4860,6 +4860,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.45.2


--nrp556lrhtfteavd--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v3 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index b5a8e3441a..65633b8bfa 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e4ed3748f9..96e7b81966 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -331,18 +331,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b939ea7ca..e45ebb24f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4808,6 +4808,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4b0ee5d10d..ae84cb522e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	Node	   *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.46.0


--muysnh7l3kienazz--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v4 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb7..1352e9de40 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d4bc5e5c08..8db000554c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, NULL, NULL);
+			}
 
-			return ExecRefreshMatView(refresh, NULL, NULL);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dadc6b630a..1be1a95cd9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4838,6 +4838,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 533fa847ab..6fa3291c64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -168,6 +168,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index cefd0d442c..47dfd88bff 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index c12f0243c9..b268237c24 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.47.0


--hht4bymbvbva5al5--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread

* [PATCH v6 3/3] Replace matview WITH OLD DATA
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 1264+ messages in thread

From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)

---
 .../sgml/ref/create_materialized_view.sgml    | 16 +++++++++--
 src/backend/commands/createas.c               | 26 +++++++++++------
 src/backend/parser/gram.y                     | 16 +++++++++++
 src/include/nodes/primnodes.h                 |  1 +
 src/test/regress/expected/matview.out         | 28 +++++++++++++++++++
 src/test/regress/sql/matview.sql              | 15 ++++++++++
 6 files changed, 90 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..1352e9de409 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     AS <replaceable>query</replaceable>
-    [ WITH [ NO ] DATA ]
+    [ WITH [ NO | OLD ] DATA ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
   <para>
    <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
    a query.  The query is executed and used to populate the view at the time
-   the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+   the command is issued (unless <command>WITH NO DATA</command> or
+   <command>WITH OLD DATA</command> is used) and may be
    refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
   </para>
 
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
    </varlistentry>
 
    <varlistentry>
-    <term><literal>WITH [ NO ] DATA</literal></term>
+    <term><literal>WITH [ NO | OLD ] DATA</literal></term>
     <listitem>
      <para>
       This clause specifies whether or not the materialized view should be
@@ -170,6 +171,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
       flagged as unscannable and cannot be queried until <command>REFRESH
       MATERIALIZED VIEW</command> is used.
      </para>
+
+     <para>
+      The form <command>WITH OLD DATA</command> keeps the already stored data
+      when replacing an existing materialized view to keep it populated.  For
+      newly created materialized views, this has the same effect as
+      <command>WITH DATA</command>.  Use this form if you want to use
+      <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as it requires
+      a populated materialized view.
+     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 9f37ed7f177..0ed44b246bd 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -332,18 +332,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
 		/* An existing materialized view can be replaced. */
 		if (is_matview && into->replace)
 		{
-			RefreshMatViewStmt *refresh;
-
 			/* Change the relation to match the new query and other options. */
-			(void) create_ctas_nodata(query->targetList, into);
+			address = create_ctas_nodata(query->targetList, into);
 
-			/* Refresh the materialized view with a fake statement. */
-			refresh = makeNode(RefreshMatViewStmt);
-			refresh->relation = into->rel;
-			refresh->skipData = into->skipData;
-			refresh->concurrent = false;
+			/*
+			 * Refresh the materialized view with a fake statement unless we
+			 * must keep the old data.
+			 */
+			if (!into->keepData)
+			{
+				RefreshMatViewStmt *refresh;
+
+				refresh = makeNode(RefreshMatViewStmt);
+				refresh->relation = into->rel;
+				refresh->skipData = into->skipData;
+				refresh->concurrent = false;
+
+				address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			}
 
-			return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+			return address;
 		}
 
 		return InvalidObjectAddress;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8be790eaccf..e405268f413 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4924,6 +4924,22 @@ CreateMatViewStmt:
 					$7->replace = true;
 					$$ = (Node *) ctas;
 				}
+		| CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+				{
+					CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+					ctas->query = $9;
+					ctas->into = $7;
+					ctas->objtype = OBJECT_MATVIEW;
+					ctas->is_select_into = false;
+					ctas->if_not_exists = false;
+					/* cram additional flags into the IntoClause */
+					$7->rel->relpersistence = $4;
+					$7->skipData = false;
+					$7->keepData = true;
+					$7->replace = true;
+					$$ = (Node *) ctas;
+				}
 		;
 
 create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index e800d13a6e0..5290e6c0e8a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
 	/* materialized view's SELECT query */
 	struct Query *viewQuery pg_node_attr(query_jumble_ignore);
 	bool		skipData;		/* true for WITH NO DATA */
+	bool		keepData;		/* true for WITH OLD DATA */
 	bool		replace;		/* replace existing matview? */
 } IntoClause;
 
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index d4b42da1ddf..f0f52dd1c6c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -751,6 +751,23 @@ SELECT * FROM mvtest_replace;
  3
 (1 row)
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a 
+---
+ 5
+(1 row)
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -905,3 +922,14 @@ ERROR:  syntax error at or near "NOT"
 LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
                                                ^
 DROP MATERIALIZED VIEW mvtest_replace;
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a  
+----
+ 17
+(1 row)
+
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index d6a4dc4b857..91f547e9cb8 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
 REFRESH MATERIALIZED VIEW mvtest_replace;
 SELECT * FROM mvtest_replace;
 
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 5 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
 -- add column
 CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
   SELECT 4 AS a, 1 b;
@@ -422,3 +430,10 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
   SELECT 1 AS a;
 
 DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Create new matview WITH OLD DATA.  This populates the new matview as if
+-- WITH DATA had been specified.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+  SELECT 17 AS a
+  WITH OLD DATA;
+SELECT * FROM mvtest_replace;
-- 
2.48.1


--aewcrbystcgftlni--





^ permalink  raw  reply  [nested|flat] 1264+ messages in thread


end of thread, other threads:[~2024-07-26 21:33 UTC | newest]

Thread overview: 1264+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2010-10-16 10:56 all keyword for pg_hba.conf host column Peter Eisentraut <[email protected]>
2010-10-16 15:03 ` Robert Haas <[email protected]>
2010-10-16 15:11 ` Brendan Jurd <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v2 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v3 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v4 3/3] Replace matview WITH OLD DATA Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v6 3/3] Replace matview WITH OLD DATA 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